# Copyright (C) 2007-2008, Open Society Institute
#                Thomas Moroz: tmoroz@sorosny.org
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License Version 2 as published
# by the Free Software Foundation.  You may not use, modify or distribute
# this program under any other version of the GNU General Public License.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
# 
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

import xapian
import logging
import transaction

from zope.interface import implements
from transaction.interfaces import ISavepointDataManager
from threading import local

logger = logging.getLogger('enfold.xapian.transaction')
info = logger.info
log_exc = logger.exception
debug = logger.debug


class QueueSavepoint:

    def __init__(self, queue):
        self.queue = queue
        self.state = queue.getQueueState()

    def rollback(self):
        self.queue.setQueueState(self.state)

class QueueTM(local):
    implements(ISavepointDataManager)

    def __init__(self, queue):
        local.__init__(self)
        self.queue = queue
        self.registered = False
        self.vote = False

    def register(self):
        if not self.registered:
            try:
                transaction.get().join(self)
                self.registered = True
                debug('registered tm %r (queue %r).', self, self.queue)
            except:
                log_exc('Exception during register (registered=%s)', 
                        self.registered)

    def savepoint(self):
        return QueueSavepoint(self.queue)

    def tpc_begin(self, transaction=None):
        try:
            if self.queue.read_store is not None:
                self.queue.read_store.reopen() # Make sure we get a fresh
                                               # view of the xapian
                                               # database.
        except:
            log_exc('Exception during QueueTM.tpc_begin')

    def commit(self, transaction=None):
        try:
            self.queue.prepare()
        except:
            log_exc('Exception during QueueTM.commit')
            raise

    def tpc_vote(self, transaction):
        pass

    def tpc_finish(self, transaction):
        ok = False
        shoud_flush = False
        self.queue.lock.acquire()
        try:
            try:
                for i in range(0, 3):
                    try:
                        should_flush = self.queue.processQueue(clear=False)
                    except xapian.NetworkError:
                        debug('A network error occurred, retrying...')
                        self.queue.write_store.reset()
                    else:
                        ok = True
                        break
                if i == 2 and not ok:
                    debug('Giving up after 3 tries...')
                    raise RuntimeError('Too many retries')
                if should_flush:
                    debug('Flushing store...')
                    self.queue.flush()
                else:
                    self.queue.write_store.close()
            finally:
                self.queue.clearQueue()
                self.queue.lock.release()
                self.registered = False
        except:
            log_exc('Exception during QueueTM.finish')
            
    def tpc_abort(self, transaction=None):
        try:
            if not self.queue.isEmpty():
                debug('Throwing away queue...')
                self.queue.clearQueue()
        finally:
            self.registered = False

    abort = tpc_abort

    def sortKey(self):
        return id(self)

